package com.zengqingfa.examples.mybatisplus.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.serializer.PropertyFilter;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.zengqingfa.examples.mybatisplus.annotation.FieldAuth;
import com.zengqingfa.examples.mybatisplus.annotation.FieldAuth2;
import com.zengqingfa.examples.mybatisplus.dto.DynamicColumnInfoDTO;
import com.zengqingfa.examples.mybatisplus.dto.UserQueryVo;
import com.zengqingfa.examples.mybatisplus.dto.UserVo;
import com.zengqingfa.examples.mybatisplus.entity.User;
import com.zengqingfa.examples.mybatisplus.enums.DynamicColumnModuleEnum;
import com.zengqingfa.examples.mybatisplus.mapper.UserMapper;
import com.zengqingfa.examples.mybatisplus.service.IDynamicColumnService;
import com.zengqingfa.examples.mybatisplus.service.UserService;
import com.zengqingfa.examples.mybatisplus.util.StringUtils;
import lombok.SneakyThrows;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.SheetUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @fileName: UserServiceImpl
 * @author: zengqf3
 * @date: 2021-3-2 14:59
 * @description:
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserMapper userMapper;

    @Override
    public IPage<User> selectUserPage1(User user) {
        return userMapper.selectUserPage1(new Page<>(1, 2), user.getName());
    }

    @Override
    public IPage<User> selectUserPage2(User user) {
        return userMapper.selectUserPage2(new Page<>(1, 2), user);
    }

    @Override
    public User selectById(Long id) {
        if (Objects.isNull(id)) {
            return null;
        }
        return userMapper.selectById(id);
    }

    @Override
    public User queryById(Long id) {
        return userMapper.queryById(id);
    }

    @Override
    public User selectPreviousRecordById(Long id) {
        return userMapper.selectPreviousRecordById(id);
    }

    @Override
    public List<User> selectByUserNameSqlInject(String userName) {
        return userMapper.selectByUserNameSqlInject(userName);
    }

    @Resource
    private IDynamicColumnService dynamicColumnService;

    @Override
    public IPage<Map> selectUserPageSpliceSql(UserQueryVo user) {
        Page<Map> page = new Page<>(user.getCurrent(), user.getSize());
        //获取到当前用户拥有的字段
        List<DynamicColumnInfoDTO> columnInfos = dynamicColumnService.queryDynamicColumn(DynamicColumnModuleEnum.USER.getType(), user.getUserId());
        if (CollectionUtil.isEmpty(columnInfos)) {
            return null;
        }
        //拼接字段
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < columnInfos.size(); i++) {
            DynamicColumnInfoDTO columnInfo = columnInfos.get(i);
            if (i == columnInfos.size() - 1) {
                stringBuffer.append(columnInfo.getDatabaseField() + " as " + columnInfo.getField());
            } else {
                stringBuffer.append(columnInfo.getDatabaseField() + " as " + columnInfo.getField() + ",");
            }
        }
        user.setSqlField(stringBuffer.toString());
        return userMapper.selectUserPageSpliceSql(page, user);
    }

    @Override
    public IPage<Map> selectUserPageWithJsonSerialized(UserQueryVo user) {
        Page<User> page = new Page<>(user.getCurrent(), user.getSize());
        //获取到当前用户拥有的字段
        List<DynamicColumnInfoDTO> columnInfos = dynamicColumnService.queryDynamicColumn(DynamicColumnModuleEnum.USER.getType(), user.getUserId());
        if (CollectionUtil.isEmpty(columnInfos)) {
            return null;
        }
        //获取到字段
        List<String> fields = columnInfos.stream().map(DynamicColumnInfoDTO::getField).distinct().collect(Collectors.toList());
        //fastjson过滤器：FastJson的属性过滤器
        /**
         * https://www.cnblogs.com/pcheng/p/11507901.html
         * 通过实现PropertyFilter接口的apply方法，来实现过滤属性，方法参数name即为字段的key，参数value即为字段的value，这种方式最强大，因为可以自定义具体过滤规则。
         * 方法返回false即为排除该字段。在转JSON的时候，带上过滤器参数即可。注：参数name指的字段名为转成JSON后的key，不是实体的属性名。
         */
        PropertyFilter profilter = new PropertyFilter() {
            @Override
            public boolean apply(Object object, String name, Object value) {
                if (fields.contains(name)) {
                    // false表示字段将被排除在外
                    return true;
                }
                return false;
            }
        };
        IPage<User> userIPage = userMapper.selectUserPageWithJsonSerialized(page, user);
        //重新序列化为json需要的字段返回
        IPage pageResult = new Page(page.getCurrent(), page.getSize(), page.getTotal());
        pageResult.setPages(page.getPages());
        List<User> records = userIPage.getRecords();
        if (CollectionUtils.isEmpty(records)) {
            return pageResult;
        }
        List<Map> results = Lists.newArrayList();
        records.forEach(r -> {
            String s = JSON.toJSONString(r, profilter);
            Map<String, String> map = JSON.parseObject(s, new TypeReference<Map<String, String>>() {
            });
            results.add(map);
        });
        pageResult.setRecords(results);
        return pageResult;
    }

    @SneakyThrows
    @Override
    public IPage<Map> selectUserPageUseCustom(UserQueryVo user) {
        Page<User> page = new Page<>(user.getCurrent(), user.getSize());
        //获取到当前用户拥有的字段
        List<DynamicColumnInfoDTO> columnInfos = dynamicColumnService.queryDynamicColumn(DynamicColumnModuleEnum.USER.getType(), user.getUserId());
        if (CollectionUtil.isEmpty(columnInfos)) {
            return null;
        }
        //获取到字段
        List<String> fieldList = columnInfos.stream().map(DynamicColumnInfoDTO::getField).distinct().collect(Collectors.toList());
        IPage<User> userIPage = userMapper.selectUserPageWithJsonSerialized(page, user);
        //重新序列化为json需要的字段返回
        IPage pageResult = new Page(page.getCurrent(), page.getSize(), page.getTotal());
        pageResult.setPages(page.getPages());
        List<User> records = userIPage.getRecords();
        if (CollectionUtils.isEmpty(records)) {
            return pageResult;
        }
        List<Map> results = Lists.newArrayList();
        Class<User> clazz = User.class;
        for (int i = 0; i < records.size(); i++) {
            User u = records.get(i);
            Map<String,String> map=new HashMap<>();
            for (String columnField : fieldList) {
                Field field = User.class.getDeclaredField(columnField);
                String itemName = StringUtils.capitalizeFirstLetter(field.getName());
                Method getMethod = clazz.getMethod("get" + itemName);
                Object value = getMethod.invoke(u);
                if (ObjectUtil.isNotNull(value) && value.getClass().isAssignableFrom(LocalDateTime.class)) {
                    map.put(field.getName(),DateUtil.formatDate((Date) value));
                }else {
                    map.put(field.getName(),String.valueOf(value));
                }

            }
            results.add(map);
        }
        pageResult.setRecords(results);
        return pageResult;
    }

    @Resource
    private HttpServletResponse response;

    @SneakyThrows
    @Override
    public void export(UserQueryVo user) {
        Page<User> page = new Page<>(user.getCurrent(), user.getSize());
        //获取到当前用户拥有的字段
        List<DynamicColumnInfoDTO> columnInfos = dynamicColumnService.queryDynamicColumn(DynamicColumnModuleEnum.USER.getType(), user.getUserId());
        //获取到字段
        IPage<User> userIPage = userMapper.selectUserPageWithJsonSerialized(page, user);
        List<User> records = userIPage.getRecords();
        if (CollectionUtil.isEmpty(records)){
            throw new RuntimeException("导出内容为空");
        }
        // 记录要导出的列名列表
        List<String> fieldList = new ArrayList<>();
        // 记录要导出的资产台账列描述列表，作为Excel表头信息
        List<Object> descList = CollUtil.newArrayList("序号");
        columnInfos.stream()
                .filter(DynamicColumnInfoDTO::getShow)
                .sorted(Comparator.comparing(DynamicColumnInfoDTO::getOrder))
                .forEach(dynamicColumnInfoDTO -> {
                    fieldList.add(dynamicColumnInfoDTO.getField());
                    descList.add(dynamicColumnInfoDTO.getDesc());
                });
        // 没有显示列，直接返回
        Assert.isFalse(CollectionUtils.isEmpty(fieldList), "查询不到可导出的内容！");
        // 记录Excel表的每行内容信息
        List<List<Object>> rows = CollUtil.newArrayList();
        rows.add(descList);
        // 遍历并拼接导出的内容列表，作为Excel表内容信息
        Class<User> clazz = User.class;
        for (int i = 0; i < records.size(); i++) {
            User u = records.get(i);
            // 记录当前Excel行数据
            List<Object> row = CollUtil.newArrayList(i + 1);
            for (String columnField : fieldList) {
                Field field = User.class.getDeclaredField(columnField);
                String itemName = StringUtils.capitalizeFirstLetter(field.getName());
                Method getMethod = clazz.getMethod("get" + itemName);
                Object value = getMethod.invoke(u);
                if (ObjectUtil.isNotNull(value) && value.getClass().isAssignableFrom(Date.class)) {
                    row.add(DateUtil.formatDate((Date) value));
                } else {
                    row.add(value);
                }
            }
            rows.add(row);
        }

        // 通过工具类创建writer，写出导出内容
        ExcelWriter writer = ExcelUtil.getWriter(true);
        writer.renameSheet("资产台账导出");
        writer.write(rows, true);
        writer.autoSizeColumnAll();

        // 加粗字体
        Font boldFont = writer.createFont();
        boldFont.setBold(true);
        // 表头样式
        CellStyle headerStyle = writer.createCellStyle(0, 0);
        headerStyle.setFont(boldFont);
        headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        headerStyle.setAlignment(HorizontalAlignment.CENTER);
        headerStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
        headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        headerStyle.setBorderBottom(BorderStyle.THIN);
        headerStyle.setBorderLeft(BorderStyle.THIN);
        headerStyle.setBorderRight(BorderStyle.THIN);
        headerStyle.setBorderTop(BorderStyle.THIN);
        // 设置表头样式与列宽
        for (int i = 0; i < writer.getColumnCount(); i++) {
            writer.setStyle(headerStyle, i, 0);
            // 手动设置列宽
            double width = SheetUtil.getColumnWidth(writer.getSheet(), i, false);
            if (BigDecimal.valueOf(width).compareTo(BigDecimal.valueOf(-1.0D)) != 0) {
                width *= 256.0D;
                //此处可以适当调整，调整列空白处宽度
                width += 2000D;
                writer.setColumnWidth(i, Math.toIntExact(Math.round(width / 256D)));
            }
        }
        // Excel文件下载
        String fileName = "AssetLedgerExportExcel.xlsx";
        response.setStatus(HttpStatus.OK.value());
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.addHeader("Content-Disposition", "attachment;fileName=\"" + URLEncoder.encode(fileName, "UTF-8") + "\"");
        ServletOutputStream out = response.getOutputStream();
        writer.flush(out, true);
        // 关闭writer，释放内存
        writer.close();
        IoUtil.close(out);
    }

    @Override
    @FieldAuth2(moudle =DynamicColumnModuleEnum.USER,clazz = User.class)
    public IPage<User> selectUserPageWithAnnotation(UserQueryVo user) {
        Page<User> page = new Page<>(user.getCurrent(), user.getSize());
        IPage<User> userIPage = userMapper.selectUserPageWithAnnotation(page, user);
        return userIPage;
    }

    @Override
    @FieldAuth(moudle =DynamicColumnModuleEnum.USER)
    public List<Map> selectUserListWithAnnotation(UserQueryVo user) {
        List<Map> useList = userMapper.selectUserListWithAnnotation(user);
        return useList;
    }

    @Override
    public IPage<User> selectUserPageWithModifyColumn(UserQueryVo user) {
        Page<User> page = new Page<>(user.getCurrent(), user.getSize());
        IPage<User> userIPage = userMapper.selectUserPageWithModifyColumn(page, user);
        return userIPage;
    }

    @Override
    public IPage<User> selectUserPageWithModifyResultSet(UserQueryVo user) {
        Page<User> page = new Page<>(user.getCurrent(), user.getSize());
        IPage<User> userIPage = userMapper.selectUserPageWithModifyResultSet(page, user);
        return userIPage;
    }
}
